
package cfar.ff.data;

import cfar.ff.fun.F1;
import cfar.ff.interfaces.Empty;
import cfar.ff.interfaces.Sequence;

import java.util.Collections;
import static cfar.ff.fun.Fun.*;
import static cfar.ff.fun.Seq.*;

import java.util.Iterator;





/**
 *
 * @author badi
 */
public class List<A> implements Sequence<List, A> {

    private fj.data.List<A> list;

    public List(){
        list = fj.data.List.nil();
    }

    private List(fj.data.List<A> list){
        this.list = list;
    }

    public List(Iterable<A> seq){
        list = fj.data.List.iterableList(seq);
    }

    public static <A> List<A> list(final A... as){
        return new List(fj.data.List.list(as));
    }

    public static <A> List<A> nil(){
        return new List(fj.data.List.nil());
    }


    public boolean equals(Object other){
        if(other instanceof List){
            List o = (List) other;

            if(this.list.isEmpty() && o.list.isEmpty())
                return true;

            else if (this.list.isNotEmpty() && o.list.isNotEmpty())
                return and(
                            zipWith(new eq(), this, o)
                          );
            else return false;
        } else return false;
    }


    public String toString(){
        if(this.length() < 1)
            return "List []";
        else {
            String me = "List [";
            for(A a : this)
                me += a + ", ";
            me = me.substring(0, me.length() - 2);
            return me + "]";
        }
    }



    public List<A> cons(A a) {
        return new List(list.cons(a));
    }

    public List<A> snoc(A a) {
        return new List(list.snoc(a));
    }

    public A head() {
        return list.head();
    }

    public List<A> tail() {
        return new List(list.tail());
    }

    public <A> List<A> pure(A a) {
        return new List(fj.data.List.single(a));
    }

    public <B> List<B> fmap(F1<A, B> f) {
        return new List(list.map(f));
    }

    public A from() {
        return list.head();
    }

    public Iterator<A> iterator() {
        return list.iterator();
    }

    public int length() {
        return list.length();
    }

    public <B> Empty<List, B> empty() {
        return new List(fj.data.List.nil());
    }

    public fj.data.List<A> getList(){
        return this.list;
    }

    public A last() {
        return list.last();
    }

    public Sequence<List, A> shuffle() {
        java.util.List<A> l = new java.util.LinkedList(list.toCollection());
        Collections.shuffle(l);
        return new List(l);
    }

}
